﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace Dou.UI
{
    /// <summary>
    /// 单选组
    /// </summary>
    public class DToggleGroup : UIBehaviour
    {
        [Serializable]
        public class ToggleGroupEvent : UnityEvent<int> { }
        
        /// <summary>
        /// 当前选中的按钮改变时，返回 true 正常选中该按钮，返回 false 会取消这次选中
        /// </summary>
        public event Func<int, bool> onChanging;
        
        [SerializeField]
        private bool _allowSwitchOff = false;
        [SerializeField]
        private ToggleGroupEvent _onChanged = new ToggleGroupEvent();
        [SerializeField]
        private List<DToggle> _toggles = new List<DToggle>();
        
        /// <summary>
        /// 设置为 true 允许所有单选按钮都是未选中状态，设置为 false 时必须有一个单选按钮处于选中状态
        /// </summary>
        public bool allowSwitchOff
        {
            set => _allowSwitchOff = value;
            get => _allowSwitchOff;
        }

        /// <summary>
        /// 当前选中项索引
        /// </summary>
        public int selectedIndex
        {
            set
            {
                if (value >= 0 && value < _toggles.Count)
                {
                    if (value != selectedIndex)
                    {
                        SetToggleOn(_toggles[value], false, false);
                    }
                }
                else if (_allowSwitchOff)
                {
                    // 全部设定为未选中
                    for (var i = 0; i < _toggles.Count; i++)
                    {
                        _toggles[i].SetIsOff();
                    }
                }
            }
            get
            {
                for (var i = 0; i < _toggles.Count; i++)
                {
                    if (_toggles[i].isOn)
                    {
                        return i;
                    }
                }
                return -1;
            }
        }
        
        /// <summary>
        /// 所有单选按钮数量
        /// </summary>
        public int toggleCount => _toggles.Count;
        
        /// <summary>
        /// 当前选中的按钮改变后
        /// </summary>
        public ToggleGroupEvent onChanged
        {
            set => _onChanged = value;
            get => _onChanged;
        }

        protected override void OnEnable()
        {
            foreach (var toggle in _toggles)
            {
                toggle.SetToggleGroup(this);
            }
            base.OnEnable();
        }
        
        protected override void Start()
        {
            EnsureValidState();
            base.Start();
        }
        
        /// <summary>
        /// 注册一个单选按钮
        /// </summary>
        public void RegisterToggle(DToggle toggle)
        {
            if (!_toggles.Contains(toggle))
            {
                _toggles.Add(toggle);
                toggle.SetToggleGroup(this);
                EnsureValidState();
            }
        }
        
        /// <summary>
        /// 注销一个单选按钮
        /// </summary>
        public void UnregisterToggle(DToggle toggle)
        {
            if (_toggles.Contains(toggle))
            {
                _toggles.Remove(toggle);
                toggle.SetToggleGroup(null);
                EnsureValidState();
            }
        }
        
        private void ValidateToggleIsInGroup(DToggle toggle)
        {
            if (toggle == null || !_toggles.Contains(toggle))
            {
                throw new ArgumentException(string.Format("Toggle {0} is not part of ToggleGroup {1}", new object[] {toggle, this}));
            }
        }
        
        /// <summary>
        /// 尝试设置单选组中的选中项
        /// </summary>
        /// <param name="toggle">选中的单选按钮</param>
        /// <param name="changingCheck">是否抛出 OnChanging 事件</param>
        /// <returns>是否可以选中传入的单选按钮</returns>
        internal bool SetToggleOn(DToggle toggle, bool changingCheck = true, bool dispatchEvent = true)
        {
            ValidateToggleIsInGroup(toggle);

            var index = _toggles.IndexOf(toggle);
            // 是否允许选中当前项
            if (changingCheck)
            {
                if (onChanging != null && !onChanging.Invoke(index))
                {
                    return false;
                }
            }
            
            // 选中当前项
            _toggles[index].SetIsOn();
            
            // 关闭其它选中项
            for (var i = 0; i < _toggles.Count; i++)
            {
                if (_toggles[i] == toggle)
                {
                    continue;
                }
                _toggles[i].SetIsOff();
            }
            
            if (dispatchEvent)
            {
                onChanged?.Invoke(index);
            }
            
            return true;
        }
        
        internal bool AnyTogglesOn()
        {
            return _toggles.Find(x => x.isOn) != null;
        }
        
        internal void EnsureValidState()
        {
            // 未选中任何单选项但是必须选中的情况，选中第一个
            if (!allowSwitchOff && !AnyTogglesOn() && _toggles.Count != 0)
            {
                SetToggleOn(_toggles[0]);
            }

            // 出现了多个选中的情况，只保留第一个选中项
            var activeToggles = ActiveToggles();
            if (activeToggles.Count() > 1)
            {
                var firstActive = GetFirstActiveToggle();
                foreach (var toggle in activeToggles)
                {
                    if (toggle == firstActive)
                    {
                        continue;
                    }
                    toggle.isOn = false;
                }
            }
        }
        
        private IEnumerable<DToggle> ActiveToggles()
        {
            return _toggles.Where(x => x.isOn);
        }
        
        private DToggle GetFirstActiveToggle()
        {
            var activeToggles = ActiveToggles();
            return activeToggles.Any() ? activeToggles.First() : null;
        }
    }
}
